Professional Documents
Culture Documents
Anda Sendiri!
Membuat komponen Delphi secara mudah dan
cepat dengan pengenalan Object Oriented
Programming
Daftar Isi
Daftar Isi ............................................................................................................................. i
Sekapur Sirih...................................................................................................................... 1
1 Mengapa Membuat Komponen Sendiri? .................................................................. 2
1.1 Apa itu komponen?.......................................................................................................2
1.2 Mengapa Kita Membuat Komponen?.........................................................................3
1.3 Apa saja yang perlu diketahui untuk membuat komponen? ....................................5
2 Object Oriented Programming (OOP) ...................................................................... 7
2.1 Apa itu OOP? ................................................................................................................7
2.2 Apa perbedaan OOP dengan Procedural Programming ..........................................8
3 Konsep-konsep Dasar OOP ..................................................................................... 10
3.1 Kelas .............................................................................................................................10
3.2 Enkapsulasi..................................................................................................................11
3.3 Pewarisan.....................................................................................................................12
3.4 Polimorfisme................................................................................................................13
4 Disain dan Implementasi Pemrograman Berorientasi Object dengan Delphi...... 14
4.1 Bermula dari Masalah, Diakhiri dengan Solusi Program.......................................14
4.1.1 Fase Analisis..........................................................................................................................14
4.1.2 Fase Disain ............................................................................................................................14
4.1.3 Fase Implementasi .................................................................................................................15
4.2 Studi Kasus – Menganalisis, Mendisain, dan Mengimplementasikan Kelas.........16
4.2.1 Analisis ..................................................................................................................................16
4.2.2 Disain.....................................................................................................................................17
4.2.3 Implementasi .........................................................................................................................20
5 Membuat Komponen Sederhana ............................................................................. 33
5.1 Langkah-langkah Membuat Komponen...................................................................34
5.1.1 Menentukan Superclass dari Komponen ...............................................................................34
5.1.2 Membuat Komponen Sederhana Langkah demi Langkah .....................................................35
6 Contoh Komponen-Komponen ................................................................................ 48
6.1 Komponen yang Terhubung ke Basisdata ................................................................48
6.1.1 TWWDBNavigator................................................................................................................48
6.1.2 TWWDBDateTimePicker...................................................... Error! Bookmark not defined.
7 Referensi................................................................................................................... 67
K
omponen pada dasarnya adalah objek-objek yang dapat dimanipulasi pada
saat kita merancang suatu program. Komponen Delphi terdiri dari objek-
objek dalam bentuk kode Pascal yang biasanya mempunyai atribut dan
fungsi yang dapat menggambarkan tingkah laku dari komponen tersebut.
Komponen tersebut biasanya diletakkan pada palet komponen dan bisa dimanipulasi di
atas form designer.
O
OP atau Object Oriented Programming (Pemrograman Berorientasi Objek)
merupakan suatu bentuk pemrograman yang memodelkan masalah dengan
pendekatan objek. Dalam bukunya yang berjudul An Introduction to
Object Oriented Programming, Timothy Budd mengutip karakteristik fundamental OOP
dari Alan Kay (yang dianggap oleh sebagian orang sebagai bapak OOP), adalah sebagai
berikut [Budd]:
1. Semua adalah objek.
2. Komputasi dilakukan dengan komunikasi antarobjek. Setiap objek berkomunikasi
dengan objek yang lain melalui pengiriman dan penerimaan pesan. Sebuah pesan
merupakan permintaan atas sekumpulan aksi dengan semua argumen yang
diperlukan untuk menyelesaikan suatu tugas tertentu.
3. Setiap objek memiliki memori sendiri, yang dapat terdiri dari objek-objek lainnya.
4. Setiap objek adalah wakil atau representasi dari suatu kelas. Sebuah kelas dapat
mewakili sekelompok objek yang sama
5. Kelas merupakan kumpulan tingkah laku yang berkaitan dengan sebuah objek.
Jadi, semua objek yang merupakan wakil dari kelas yang sama dapat melakukan
aksi yang sama pula.
6. Kelas-kelas diorganisasi ke dalam struktur pohon yang berakar tunggal,
dinamakan jenjang pewarisan (inheritance hierarchy). Memori dan tingkah laku
yang berkaitan dengan wakil dari sebuah kelas secara otomatis tersedia pada tiap
kelas yang berasosiasi dengan sebuah turunan dalam struktur pohon ini. Jenjang
ini bisa juga dipandang sebagai jenjang piramida, dengan kelas super atau kelas
utama menduduki peringkat teratas dari semua kelas turunannya.
Kelas Tertinggi
Kelas Terendah
Membuat Komponen Sendiri dengan Delphi – Wisnu Widiarta
Halaman 7
Sifat kelas pada tingkat tertinggi terbawa hingga ke kelas pada tingkatan yang paling
rendah, yang menunjukkan konsep pewarisan atau inheritance
Dengan menginstruksikan tugas khusus dan terbatas pada setiap objek, programmer dapat
memecah masalah pemrograman menjadi bagian-bagian kecil sehingga dapat mengatasi
kompleksitas permasalahan dengan lebih mudah. Setiap objek pada umumnya memiliki
tiga sifat, yaitu keadaan, operasi, dan identitas objek ([Horstmann] hal. 1). Sebuah objek
dapat menyimpan informasi sebagai hasil operasi sebelumnya. Informasi tersebut
menentukan bagaimana objek melakukan operasi selanjutnya. Koleksi dari seluruh
informasi yang dimiliki objek pada suatu saat merupakan keadaan objek pada saat itu.
Informasi tersebut pada akhirnya memberikan identitas khusus yang membedakan suatu
objek dengan objek lainnya.
Global Data
Fungsi Fungsi
Fungsi
Fungsi
Procedural Programming
method
data
method
method data
method
method
data
method
K
arena komponen pada dasarnya adalah objek, maka memahami konsep
OOP jelas merupakan sesuatu yang esensial. Kita akan membahas
secara singkat mengenai hal-hal yang berkaitan dengan OOP sebagai
bekal utama dalam langkah pembuatan komponen yaitu:
− Kelas (class abstraction)
− Enkapsulasi (encapsulation)
− Pewarisan (inheritance)
− Polimorfisme (polymorphism)
3.1 Kelas
Kelas merupakan deskripsi abstrak informasi dan tingkah laku dari sekumpulan
data. Kelas juga dikenal sebagai tipe objek. Karenanya, representasi atau wakil dari suatu
kelas adalah objek kelas tersebut. Sebuah kelas merupakan tipe data yang
mengenkapsulasi data dan operasi pada data dalam suatu unit tunggal. Berbeda dengan
procedural programming yang memisahkan antara data dan fungsi sebagai elemen yang
berbeda.
Suatu kelas mendifinisikan suatu struktur yang terdiri atas data kelas (data field),
prosedur atau fungsi kelas (method), dan sifat kelas (property). Sebuah field pada
dasarnya adalah sebuah variabel bagian dari kelas. Method merupakan semua prosedur
yang berasosiasi dengan kelas tersebut. Ada method yang dipanggil melalui objek dari
kelas, dan ada pula method yang dipanggil melalui kelas itu sendiri (hal ini akan dibahas
lebih lanjut kemudian). Property adalah antarmuka untuk data yang berkaitan dengan
objek dari kelas. Property memiliki cara khusus untuk mengaksesnya, yang dikenal
dengan access specifiers. Di luar objek itu, property tampak pada dasarnya hampir sama
seperti field. Istilah-istilah ini akan dijelaskan secara lebih jelas kemudian.
Setiap objek secara dinamis mengalokasikan sejumlah memori dengan struktur
yang telah ditentukan oleh tipe kelasnya. Setiap objek memiliki salinan yang bersifat
tunggal untuk setiap field yang didefinisikan dalam kelas tersebut. Berbeda halnya
3.2 Enkapsulasi
Istilah enkapsulasi sebenarnya adalah kombinasi data dan fungsionalitas dalam
sebuah unit tunggal sebagai bentuk untuk menyembunyikan detail informasi. Inilah salah
satu konsep dalam OOP yang tidak terdapat pada procedural programming. Tipe data
record pada Pascal atau struct pada C hanya mengumpulkan data namun tidak untuk
fungsi atau operasi. Kelas menyatukan data dengan operasi dalam satu kesatuan. Sebagai
tambahan dalam enkapsulasi, OOP lebih dikarakterisasikan dengan pewarisan
(inheritance) dan polimorfisme (polymorphism). Proses enkapsulasi memudahkan kita
untuk menggunakan sebuah objek dari suatu kelas karena kita tidak perlu mengetahui
segala hal secara rinci. Enkapsulasi menekankan pada antarmuka suatu kelas, atau dengan
kata lain bagaimana menggunakan objek kelas tertentu. Sebuah contoh, kelas Mobil
menyediakan antarmuka fungsi untuk menjalankan mobil tersebut, tanpa kita perlu tahu
komposisi bahan bakar, udara, dan kalor yang diperlukan untuk proses tersebut. Disini
terjadi proses enkapsulasi terhadap rincian bagaimana sebuah mobil dijalankan.
3.4 Polimorfisme
S
emuanya bermula dari permasalahan. Sebelum membuat program, ada tiga
langkah utama yang bisa terjadi secara berkesinambungan dan membentuk
daur tertentu setelah kita mendapatkan suatu masalah untuk dipecahkan
dengan program. Pertama, adalah analisis permasalahan itu sendiri. Kedua, kita
melakukan disain bagaimana kita akan memecahkan masalah tersebut. Ketiga, adalah
implementasi disain yang telah kita buat. Terkadang, kita harus kembali ke tahap pertama
dan tahap kedua jika memang diperlukan.
Pada fase ini biasanya pemahaman masalah yang masih samar-samar diubah
menjadi deskripsi spesifik yang berisi tentang masalah-masalah yang akan dipecahkan.
Misalnya kita akan membuat sebuah program permainan Halma. Pada fase ini, harus
dijelaskan aturan permainan yang tepat dan tidak mengandung kontradiksi. Jika beberapa
daerah mempunyai aturan permainan Halma yang berbeda-beda, fase ini harus
menghasilkan aturan yang baku dan dijadikan suatu panduan untuk fase selanjutnya. Hal
lain yang harus didokumentasikan adalah bagaimana pemain akan berinteraksi dengan
program. Bagaimana bila akan dibuat versi jaringan, dan bagaimana pula jika dilengkapi
dengan pemain komputer dengan menggunakan teknologi kecerdasan buatan, dan
sebagainya. [Horstmann] pada halaman 5 menekankan bahwa fase ini adalah penjelasan
apa yang harus dilakukan dan bukan bagaimana sesuatu harus dilakukan.
Pada fase ini programmer mengubah disain kelas-kelas menjadi untain kode
program ke dalam bahasa pemrograman tertentu yang telah ditentukan. Setelah itu kode-
kode tersebut diuji dan pada akhirnya diintegrasikan menjadi satu solusi program akhir.
Pada beberapa permasalahan yang cukup kompleks untuk dimengerti, adanya
sebuah prototipe program akan sangat membantu bahkan pada fase disain maupun
analisis sekalipun. Dengan adanya prototipe ini, diharapkan banyak gagasan akan muncul
sehingga solusi yang diinginkan benar-benar dapat tercapai.
[Horstmann] mengingatkan agar kita tidak terburu-buru dalam fase analisis dan
disain semata-mata hanya untuk mengejar pembuatan prototipe. Sebaliknya jangan pula
kita ragu-ragu untuk kembali ke fase sebelumnya apabila prototipe benar-benar
memberikan kita gagasan baru dalam memecahkan masalah.
Berikut ini kita akan coba untuk membuat kelas-kelas yang berkaitan dengan bangun
datar, seperti persegi panjang, lingkaran, dan segitiga dan semuanya dapat digunakan
untuk menghitung luas dan panjang kelilingnya. Kita akan mencoba untuk menganalisis,
mendisain, dan mengimplementasikan ketiga kelas di atas.
4.2.1 Analisis
4.2.2 Disain
Jika kita daftarkan semua operasi dan atribut yang berkaitan dengan masing-masing
bangun datar tersebut maka kita akan dapatkan tabel berikut ini:
Setelah kita perhatikan, ternyata ditemukan bahwa ketiga kelas tersebut memiliki
dua method yang selalu muncul, yaitu cariLuas dan cariKeliling. Kita bisa
menggunakan konsep inheritance dan generalisation (pewarisan dan generalisasi) untuk
ketiga kelas tersebut. Ketiganya adalah merupakan bangun datar. Kelas yang akan
ditambahkan adalah kelas BangunDatar yang dapat dijadikan kelas dasar untuk ketiga
kelas tersebut. Karena kelas bangun datar sangat luas, dan tidak dapat diketahui
bentuknya, maka kelas bangun datar dapat digolongkan sebagai kelas abstrak. Kelas ini
tidak berguna untuk diinstantiasikan atau dibuat objeknya (jika kita memanggil member
dari kelas abstrak, akan menimbulkan exception EAbstractError) untuk komponen yang
mengandung method abstract tidak bisa dibuat instancenya. Kelas ini harus diturunkan
terlebih dahulu menjadi kelas yang lebih kongkrit, misalnya kelas Lingkaran,
PersegiPanjang, Segitiga, atau misalnya kelas JajaranGenjang. Pada diagram berikut ini
kita bisa melakukan pull up method, yaitu menarik method yang ada pada suatu kelas dan
meletakkannya pada kelas supernya. Dengan menggunakan kelas abstrak ini, jika suatu
waktu kita akan membuat kelas yang merupakan bangun datar, maka kita dapat
menurunkannya langsung dari kelas abstrak BangunDatar yang telah kita buat. Dan jika
kita tahu ada sebuah kelas merupakan turunan dari kelas BangunDatar, maka kita tahu
persis bahwa kelas tersebut pasti memiliki method/fungsi/operasi cariLuas dan
cariKeliling. Artinya, ada semacam kontrak yang mengatakan bahwa jika sebuah kelas
Abstract Class
BangunDatar
Method
cariLuas
cariKeliling
Lambang pewarisan
Karena semua method pada kelas BangunDatar bersifat abstract, maka kita juga bisa
membuat BangunDatar sebagai interface. Interface dikenal juga sebagai kelas abstrak
murni. Jika kelas abstrak bisa memiliki sebagian method yang kongkrit dan abstrak, maka
interface hanya memiliki method yang dideklarasikan sebagai abstrak.
Component Palette
Object TreeView
Form Designer
Object Inspector
Keterangan:
1. Form Designer merupakan wadah (container) yang dijadikan sebagai tempat
untuk meletakkan semua komponen visual yang akan tampak pada program yang
akan kita buat. Pada saat kita buka Delphi pertama kali, otomatis kita akan
mendapatkan sebuah objek dari kelas TForm
2. Component Palette merupakan tempat untuk meletakkan semua komponen yang
siap digunakan pada program delphi kita
3. Object Inspector merupakan panel untuk menginspeksi objek, dengan kata lain
melihat atribut-atribut dari sebuah objek sehingga kita bisa mengubahnya pada
saat design time
4. Object Tree View merupakan panel untuk melihat hirarki objek-objek yang
diletakkan pada form kita. Hal ini akan memudahkan kita untuk memilih objek
yang sulit untuk diklik ketika akan dipilih, dan melihat parent dari sebuah objek.
Sebuah program dibangun dari modul-modul kode program yang disebut unit. Setiap unit
disimpan dalam sebuah file terpisah, dan dikompilasi secara terpisah pula. Dengan unit, kita
dapat :
− Membagi program yang besar ke dalam modul-modul, sehingga bisa diedit secara
terpisah
− Membuat library yang bisa digunakan oleh banyak program
− Library yang telah dikompilasi dapat didistribusikan kepada programmer lain, tanpa harus
menyertakan kode program yang kita buat
Pada gambar di atas, pada waktu kita membuat sebuah unit, akan tampak bagian unit
(unit merupakan reserved word atau kata kunci), interface, implementation, dan end.
Nama unit tersebut secara default diberi nama Unit1, yang otomatis akan disimpan dalam
file Unit1.PAS dan setelah dikompilasi akan menghasilkan file Unit1.DCU (delphi
compiled unit). Nama unit harus unik dalam sebuah program, artinya tidak boleh ada dua
unit yang memiliki nama yang sama, meskipun diletakkan dalam folder yang berbeda.
Bagian interface dimulai dari kata kunci interface dan diakhir pada kata kunci
implementation. Dalam bagian ini, kita bisa mendeklarasikan konstanta, tipe data,
Penjelasan:
− File classBangunDatar.PAS memiliki unit classBangunDatar
− Unit tersebut memiliki sebuah kelas bernama TBangunDatar yang merupakan
turunan dari kelas TObject. Kita bisa saja menuliskan class(TObject) pada contoh
di atas dengan class saja. Sebab jika kita hanya memberikan kata class,
secara default kelas tersebut merupakan turunan dari kelas TObject. Untuk
kejelasan, saya sengaja selalu menggunakan TObject untuk semua kelas turunan
dari kelas TObject
− Kata kunci public menunjukkan bahwa function cariLuas dan
cariKeliling dapat diakses semua unit
dapat diubah oleh kelas turunannya, dan dideklarasikan abstract, artinya tidak
ada implementasinya pada kelas TBangunDatar, dan implementasinya diserahkan
pada kelas turunannya
Perbandingan pada public abstract class BangunDatar extends Object {
public abstract double cariLuas();
pemrograman Java
public abstract double cariKeliling();
(disimpan pada file }
BangunDatar.java)
Kelas pada Delphi hampir sama dengan Java, pada dasarnya adalah pointer. Compiler
secara otomatis menyembunyikannya, sehingga kita tidak perlu menggunakan operator
pointer. Mengetahui status kelas sebagai pointer pada dasarnya penting ketika kita
membuat fungsi dengan parameter sebuah kelas. Pada umumnya, kita seharusnya
Untuk menghasilkan kode pada bagian implementasi, kita dapat mengetikkan secara
manual atau meletakkan kursor pada akhir baris prototipe fungsinya, dan menekan
tombol CTRL + SHIFT + C.
Untuk menambahkan akses pada panjang dan lebar dari persegi panjang, kita
perlu memberikan aksesor dan mutator pada variabel panjang dan lebar. Kedua variabel
itu tetap dalam batasan private artinya hanya bisa diakses dari dalam unit tersebut. Ada
dua cara untuk melakukannya di Delphi, yaitu dengan public method untuk aksesor dan
interface
uses classBangunDatar;
type
TPersegiPanjang = class(TBangunDatar)
private
FPanjang: double;
// Konvensi variable private pada Delphi diawali dengan F
FLebar: double;
public
function cariLuas:double; override;
function cariKeliling:double; override;
//dengan menggunakan property
property Panjang:double read FPanjang write FPanjang;
property Lebar:double read FLebar write FLebar;
end;
implementation
{ TPersegiPanjang }
end.
unit classPersegiPanjang;
interface
uses classBangunDatar;
type
TPersegiPanjang = class(TBangunDatar)
private
FPanjang: double;
FLebar: double;
public
function cariLuas:double; override;
function cariKeliling:double; override;
function getPanjang:double;
function getLebar:double;
procedure setPanjang(panjang:double);
procedure setLebar(lebar:double);
end;
implementation
end.
Alternatif lainnya adalah melakukan kombinasi antara pemanfaatan variabel private dan
method private pada penggunaan property :
type
TPersegiPanjang = class(TBangunDatar)
private
FPanjang: double;
FLebar: double;
procedure setPanjang(panjang:double);
procedure setLebar(lebar:double);
public
function cariLuas:double; override;
function cariKeliling:double; override;
property Panjang:double read FPanjang write setPanjang;
property Lebar:double read FLebar write setLebar;
end;
Saya sendiri lebih menyukai versi final dari kelas PersegiPanjang berikut ini, yang
dilengkapi dengan constructor.
Constructor merupakan method khusus yang digunakan untuk menciptakan dan
menginisialisasikan sebuah objek. Deklarasinya hampir sama dengan procedure atau
function, hanya saja keyword pendahulunya adalah constructor. Meskipun
deklarasinya tidak mengembalikan nilai, namun sebuah constructor mengembalikan
sebuah reference ke objek yang dibuat. Secara konvensional, nama constructor adalah
Create (tidak seperti pada Java atau C++ yang mengharuskan nama constructor sama
dengan nama kelasnya). Semua data field otomatis akan bernilai 0 untuk tipe ordinal,
kosong untuk String, unsassigned untuk Variant, dan nil untuk pointer atau tipe kelas.
Jadi tidak perlu melakukan inisialisasi dengan nilai tersebut pada constructor.
Self adalah pointer ke objek kelas yang bersangkutan, pada C++ dan Java
unit FormUtama;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ExtCtrls;
type
TformBangunDatar = class(TForm)
memoOutput: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
formBangunDatar: TformBangunDatar;
{$R *.dfm}
bangunDatar := TLingkaran.Create(7);
memoOutput.Lines.Add(Format('Keliling lingkaran dengan jarijari = %d ' +
'adalah %g', [7, bangunDatar.cariKeliling]));
memoOutput.Lines.Add(Format('Luas lingkaran dengan jarijari = %d ' +
'adalah %g', [7, bangunDatar.cariLuas]));
Sampai tahap ini kita telah mencoba memulai untuk membuat aplikasi di Delphi dengan
pendekatan berorientasi pada Objek. Masih banyak konsep OOP lainnya yang belum
ditemui, dan akan saya jelaskan sedikit demi sedikit sambil learning by example. Jadi
penjelasan akan diberikan ketika dibutuhkan pada saat studi kasus dan ketika dibutuhkan.
K ini saatnya kita mencoba untuk membuat komponen sederhana dengan Delphi.
Ada beberapa hal yang perlu kita ketahui dalam pembuatan komponen. Pertama-
tama kita perlu tahu bahwa ada beberapa struktur dalam sebuah komponen.
Kita harus dapat membedakan antara kelas dengan komponen. Kelas adalah struktur atau
tipe data dalam Object Pascal, sedangkan komponen adalah sebuah kelas yang dapat
dimanipulasi dalam Delphi Environment.
Selain itu kita perlu tahu mengenai konsep kepemilikan (ownership) dan juga pewadahan
(parenthood). Sebuah komponen bisa memiliki komponen lainnya. Pemilik sebuah
komponen ditentukan dengan property Owner. Ketika sebuah komponen memiliki
komponen lainnya, komponen tersebut bertanggungjawab membebaskan alokasi memori
komponen yang dimilikinya ketika komponen itu juga sedang dibebaskan alokasi
memorinya. Parenthood hanya dimungkinkan pada komponen turunan dari TWinControl
yang bisa berfungsi sebagai parent (wadah). Komponen parent bertangunggjawab untuk
untuk memanggil method komponen anaknya untuk memaksa mereka menggambar diri
Ada beberapa check list yang perlu kita perhatikan ketika kita membuat komponen
sendiri:
− Apakah komponen yang akan kita buat cukup unik dan akan berguna untuk kita
sendiri atau programmer lain?
− Sudah jelaskah algoritma yang akan kita gunakan nantinya?
− Sudahkah saya melakukan testing untuk komponen tersebut sebelum diletakkan
dalam Palet Komponen?
Sebagai contoh, kita akan membuat komponen yang sangat sederhana, yaitu sebuah
komponen non visual. Idenya adalah membuat sebuah komponen yang:
− bernama TPersegiPanjangSederhana
− memanfaatkan kelas TPersegiPanjang yang sudah dibuat pada bab sebelumnya
− tidak memiliki visualisasi di atas form
− property Panjang dan Lebar harus bisa diakses dari Object Inspector
− Panjang dan Lebar harus lebih dari 0, jika tidak akan berubah secara otomatis
menjadi masing-masing 1 satuan panjang
− Memiliki fungsi untuk mencari luas dan keliling yang bisa digunakan pada saat
run time
unit classPersegiPanjangSederhana;
interface
{
Kita membutuhkan unit Classes karena kita menggunakan
TComponent sebagai superclass komponen
TPersegiPanjangSederhana dan membutuhkan classPersegiPanjang
karena kita membutuhkan sebuah variabel private bertipe
TPersegiPanjang yang dideklarasikan pada unit
classPersegiPanjang.
}
type
TPersegiPanjangSederhana = class(TComponent)
private
persegiPanjang:TPersegiPanjang;
procedure Register;
{
Prosedure ini digunakan ketika kita akan mendaftarkan
komponen ini ke Palet Komponen
implementation
uses Dialogs;
procedure Register;
begin
RegisterComponents('Wisnu Widiarta',
[TPersegiPanjangSederhana]);
{
Komponen TPersegiPanjang akan didaftarkan pada komponen
palet pada halaman dengan nama Wisnu Widiarta
}
end;
{
Kita menggunakan unit Dialogs karena kita menggunakan fungsi
showMessage pada implementasi. Perhatikan bahwa uses pada
bagian interface biasanya mengacu pada unit yang mengandung
tipe data yang diperlukan pada waktu deklarasi tipe data,
sedangkan uses setelah implementation mengacu pada
pemanggilan fungsi atau prosedur pada implementasi kelas
}
{ TPersegiPanjangSederhana }
constructor TPersegiPanjangSederhana.Create(AOwner:
TComponent);
var
lebarDefault, panjangDefault:double;
begin
inherited Create(AOwner);
{
Pada umumnya pemanggilan inherited Create di atas
Selalu dilakukan sebelum kita melakukan inisialisasi
data field. Dengan melakukannya, kita memanggil
constructor Create pada superclass, dan memastikan
bahwa semua data pada superclass telah terinisialisasi
dengan baik
}
lebarDefault := 1.0;
panjangDefault := 1.0;
persegiPanjang := TPersegiPanjang.Create(panjangDefault,
lebarDefault);
end;
destructor TPersegiPanjangSederhana.Destroy;
begin
persegiPanjang.Free;
inherited;
{
Pada umumnya pemanggilan inherited pada destructor
Dilakukan setelah kita membebaskan alokasi memori
untuk semua data field kelas kita sendiri
}
end;
end.
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
type
TformTest = class(TForm)
btnTest: TButton;
edPanjang: TEdit;
Label1: TLabel;
edLebar: TEdit;
Label2: TLabel;
Label3: TLabel;
lblLuas: TLabel;
Label4: TLabel;
lblKeliling: TLabel;
procedure btnTestClick(Sender: TObject);
private
{ Private declarat0ions }
public
{ Public declarations }
end;
var
formTest: TformTest;
implementation
uses classPersegiPanjangSederhana;
{$R *.dfm}
end.
− Kita perlu membuat ICON untuk komponen ini. Cara yang paling
mudah adalah menggunakan Image Editor (Klik menu Tools Image
Editor)
− Selalu pilih ukuran 24 x 24 dan VGA (16 Colors) untuk hasil terbaik di
semua komputer
− Selesai, hasilnya bisa dilihat pada palet komponen, pada Page Wisnu
Widiarta
Jika kita memberi nilai negatif pada property Lebar, maka akan muncul pesan bahwa
Lebar harus lebih dari 0, dan property Lebar ini akan diberi nilai 1. Hal yang sama
berlaku untuk property Panjang.
Sebagian besar upaya untuk membuat komponen lebih kurang seperti pada
langkah-langkah di atas. Berikutnya kita akan lihat pembuatan komponen-komponen
lainnya sebagai bahan latihan. Beberapa materi dan informasi baru akan disertakan
setahap demi setahap.
Ketika membuat aplikasi basisdata, kita sering menggunakan komponen data aware,
yang biasanya terletak pada palet komponen bagian Data Control. Salah satu komponen
yang paling sering digunakan adalah TDBNavigator, digunakan untuk menavigasi ketika
kita akan memanipulasi suatu tabel. Waktu saya menggunakan komponen ini, terkadang
ingin sekali bisa mengganti icon yang ada dengan icon yang saya miliki sendiri. Kadang
pula saya menginginkan komponen navigasi tersebut dapat memiliki Caption sehingga
lebih memudahkan bagi pengguna dalam menggunakan komponen tersebut. Dengan
berbekal nekad dan tekad bulat, akhirnya saya memberanikan diri untuk mempelajari
kode program TDBNavigator, dan berusaha memodifikasinya. Tentu saja hasilnya masih
bisa untuk disempurnakan. Idenya adalah sebagai berikut:
− Membuat TDBNavigator yang bisa diganti-ganti iconnya
− Bisa diberi Caption, dalam bahasa Indonesia maupun bahasa Inggris
− Caption bisa muncul bisa tidak, seperti halnya dengan icon
− Ketika menekan tombol tambah atau hapus, harus ada event yang tertrigger
sebelum perintah insert atau delete dilakukan ke tabel (karena event OnClick
pada TDBNavigator bawaan Delphi terpicu setelah perintah tersebut dilakukan.
Dan terkadang, kita perlu melakukan sesuatu sebelum benar-benar menghapus
atau menambah data. Misalnya untuk memeriksa apakah boleh menghapus atau
menambah data)
unit classWWDBNavigator;
{*********************************************************************
* Nama program : WWDBNavigator *
* Deskripsi : Membuat komponen yang mirip dengan DBNavigator *
* dilengkapi dengan Caption dan Glyph sekaligus *
* Tanggal : 14 September 2002 *
* Revisi akhir : 24 Oktober 2005 *
* Programmer : Wisnu Widiarta *
* *
**********************************************************************}
interface
uses
Windows, Messages, SysUtils, Classes, Controls, ExtCtrls, Buttons,
Dialogs,
Variants, ImgList, Db;
type
TWWBtnKind = (wbkFirst, wbkPrior, wbkNext, wbkLast, wbkInsert,
wbkDelete,
wbkEdit, wbkPost, wbkCancel, wbkRefresh);
{ TWWBtnKind merupakan tipe enumerasi yang mewakili fungsi masing-
masing
tombol }
TWWDBNavigator = class;
{ forward declaration, untuk menunjukkan bahwa TWWDBNavigator adalah
sebuah
kelas }
{ TWWDataLink }
TWWDataLink = class(TDataLink)
private
FNavigator: TWWDBNavigator;
protected
procedure EditingChanged; override;
procedure DataSetChanged; override;
procedure ActiveChanged; override;
public
constructor Create(ANav: TWWDBNavigator);
destructor Destroy; override;
end;
{ TWWDBnavigator }
TWWDBNavigator = class(TPanel)
private
{ Private declarations }
protected
{ Protected declarations }
procedure ActiveChanged;
procedure EditingChanged;
procedure DataChanged;
procedure Notification(AComponent: TComponent; Operation:
TOperation); override;
{ keempat prosedur di atas diadaptasi dari versi TDBNavigator untuk
menangkap
perubahan data dan Notification (turunan dari TComponent) }
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
procedure Register;
implementation
uses StdCtrls,
classIndonesianDialog { untuk menampilkan konfirmasi berbahasa
Indonesia };
procedure Register;
begin
RegisterComponents('Wisnu Widiarta', [TWWDBNavigator]);
end;
Constructor TWWDBNavigator.Create(AOwner:TComponent);
var
i:integer;
msg:array[TWWBtnKind] of String;
begin
inherited Create(AOwner);
FHints := TStringList.Create;
if Fhints.Count = 0 then
with FHints do
begin
Add('First record');
Add('Previous record');
Add('Next record');
Add('Last record');
Add('Insert record');
Add('Delete record');
Add('Edit record');
Add('Save record');
Add('Cancel record');
Add('Refresh record');
end;
TStringList(FHints).OnChange := OnHintsChange;
{ melakukan assignment event saat run time }
FCaptions := TStringList.Create;
if FCaptions.Count = 0 then
begin
msg[wbkFirst] := 'First';
msg[wbkPrior] := 'Prior';
msg[wbkNext] := 'Next';
msg[wbkLast] := 'Last';
msg[wbkInsert] := 'Insert';
msg[wbkDelete] := 'Delete';
msg[wbkPost] := 'Save';
msg[wbkCancel] := 'Cancel';
msg[wbkRefresh] := 'Refresh';
msg[wbkEdit] := 'Edit';
OnResize := DoNavigatorResize;
BevelInner := bvNone;
BevelOuter := bvNone;
FImageChangeLink := TChangeLink.Create;
FImageChangeLink.OnChange := ImageListChange;
FDataLink := TWWDataLink.Create(Self);
FCaptionEnabled := True;
end;
procedure TWWDBNavigator.ActiveChanged;
var
I: TWWBtnKind;
begin
if not (Enabled and FDataLink.Active) then
for I := Low(FBtnNavigators) to High(FBtnNavigators) do
FBtnNavigators[I].Enabled := False
else
begin
DataChanged;
EditingChanged;
end;
end;
procedure TWWDBNavigator.DataChanged;
var
UpEnable, DnEnable: Boolean;
begin
UpEnable := Enabled and FDataLink.Active and not
FDataLink.DataSet.BOF;
DnEnable := Enabled and FDataLink.Active and not
FDataLink.DataSet.EOF;
FBtnNavigators[wbkFirst].Enabled := UpEnable;
FBtnNavigators[wbkPrior].Enabled := UpEnable;
procedure TWWDBNavigator.DoNavigatorClick(Sender:TObject;
btnKind:TWWBtnKind);
begin
if Assigned(FOnNavigatorClick) then
FOnNavigatorClick(Self, btnKind);
{ ketika tombol diklik }
end;
procedure TWWDBNavigator.EditingChanged;
var
CanModify: Boolean;
begin
CanModify := Enabled and FDataLink.Active and
FDataLink.DataSet.CanModify;
FBtnNavigators[wbkInsert].Enabled := CanModify;
FBtnNavigators[wbkEdit].Enabled := CanModify and not
FDataLink.Editing;
FBtnNavigators[wbkPost].Enabled := CanModify and FDataLink.Editing;
FBtnNavigators[wbkCancel].Enabled := CanModify and
FDataLink.Editing;
FBtnNavigators[wbkRefresh].Enabled := CanModify;
end;
function TWWDBNavigator.GetDataSource:TDataSource;
begin
Result := FDataLink.DataSource;
end;
procedure TWWDBNavigator.SetDataSource(aDataSource:TDataSource);
begin
FDataLink.DataSource := aDataSource;
if not (csLoading in ComponentState) then
ActiveChanged;
if aDataSource <> nil then aDataSource.FreeNotification(Self);
end;
procedure TWWDBNavigator.OnButtonClick(Sender:TObject);
var
index:TWWBtnKind;
begin
Self.SetFocus;
index := TWWBtnKind((Sender as TSpeedButton).Tag);
with DataSource.DataSet do
begin
case Index of
wbkPrior: Prior;
wbkNext: Next;
wbkFirst: First;
wbkLast: Last;
wbkInsert: Insert;
wbkEdit: Edit;
wbkCancel: Cancel;
wbkPost: Post;
wbkRefresh: Refresh;
wbkDelete:
if FIndonesianLanguage then
begin
if (not FConfirmDelete) or
(TIndonesianDialog.MessageDlg(FDeleteRecordQuestion, mtConfirmation,
mbOKCancel) <> mrCancel) then Delete;
end
else
begin
if (not FConfirmDelete) or
(MessageDlg(FDeleteRecordQuestion,
mtConfirmation, mbOKCancel, 0) <> mrCancel) then Delete;
end;
end;
end;
end;
if not (csDesigning in ComponentState) and
Assigned(FOnNavigatorClick) then
DoNavigatorClick(Self, index);
procedure TWWDBNavigator.OnHintsChange(Sender:TObject);
var
i:integer;
procedure TWWDBNavigator.OnCaptionsChange(Sender:TObject);
var
i:integer;
begin
for i:=Ord(wbkFirst) to Ord(wbkRefresh) do
begin
with FBtnNavigators[TWWBtnKind(i)] do
if i < FCaptions.Count then
begin
if FCaptionEnabled then
Caption := FCaptions.Strings[i]
else
Caption := '';
end
else
Caption := '';
end;
end;
function TWWDBNavigator.VisibleButtonCount:integer;
var
i:TWWBtnKind;
begin
Result := 0;
for i:=wbkFirst to wbkRefresh do
if i in FVisibleButtons then
Inc(Result);
end;
procedure TWWDBNavigator.SetButtonWidth(aWidth:integer);
var
i, leftPos:integer;
begin
if VisibleButtonCount > 0 then
aWidth := inherited Width div VisibleButtonCount
else
aWidth := inherited Width div 10;
Left := LeftPos;
LeftPos := LeftPos + aWidth;
end;
end;
end;
FButtonWidth := aWidth;
if Self.VisibleButtonCount > 0 then
Width := FBtnNavigators[TWWBtnKind(0)].Width *
Self.VisibleButtonCount;
end;
end;
procedure TWWDBNavigator.SetButtonHeight(aHeight:integer);
var
i:integer;
begin
if aHeight < 1 then
Raise Exception.Create('TWWDBNavigator: minimum value for this
property is 1.')
else
begin
for i:=Ord(wbkFirst) to Ord(wbkRefresh) do
begin
with FBtnNavigators[TWWBtnKind(i)] do
begin
Visible := TWWBtnKind(i) in FVisibleButtons;
Height := aHeight;
end;
end;
FButtonHeight := aHeight;
Height := aHeight;
end;
end;
procedure TWWDBNavigator.SetCaptions(value:TStrings);
var
i:integer;
begin
FCaptions.Assign(value);
if value <> nil then
begin
for i:=Ord(wbkFirst) to Ord(wbkRefresh) do
begin
with FBtnNavigators[TWWBtnKind(i)] do
if i < FCaptions.Count then
Caption := FCaptions.Strings[i]
else
Caption := '';
procedure TWWDBNavigator.SetDeleteRecordQuestion(aQuestion:String);
begin
if Trim(aQuestion) = '' then
begin
if ConfirmDelete = True then
FDeleteRecordQuestion := 'Do you want to delete this
record?'
else
FDeleteRecordQuestion := '';
end
else
FDeleteRecordQuestion := aQuestion;
end;
procedure TWWDBNavigator.SetHints(value:TStrings);
var
i:integer;
begin
FHints.Assign(value);
if value <> nil then
begin
for i:=Ord(wbkFirst) to Ord(wbkRefresh) do
begin
with FBtnNavigators[TWWBtnKind(i)] do
if i < FHints.Count then
Hint := FHints.Strings[i]
else
Hint := '';
end;
end;
end;
procedure TWWDBNavigator.SetWidth(aWidth:integer);
var
i, leftPos:integer;
begin
if aWidth < 1 then
Raise Exception.Create('TWWDBNavigator: minimum value for this
property is 1.')
else
begin
leftPos := 0;
for i:=Ord(wbkFirst) to Ord(wbkRefresh) do
begin
with FBtnNavigators[TWWBtnKind(i)] do
begin
Visible := TWWBtnKind(i) in FVisibleButtons;
if Visible then
begin
Left := leftPos;
Width := aWidth div Self.VisibleButtonCount;
leftPos := leftPos + Width;
end;
end;
end;
procedure TWWDBNavigator.SetHeight(aHeight:integer);
var
i:integer;
begin
if aHeight < 1 then
Raise Exception.Create('TWWDBNavigator: minimum value for this
property is 1.')
else
begin
for i:=Ord(wbkFirst) to Ord(wbkRefresh) do
with FBtnNavigators[TWWBtnKind(i)] do
begin
Visible := TWWBtnKind(i) in FVisibleButtons;
Height := aHeight;
end;
FHeight := aHeight;
FButtonHeight := aHeight;
inherited Height := aHeight;
end;
end;
procedure TWWDBNavigator.SetVisibleButtons(value:TVisibleButtons);
var
i, leftPos, shouldBeVisibleCount:integer;
begin
leftPos := 0;
FVisibleButtons := value;
shouldBeVisibleCount := 0;
for i:=Ord(wbkFirst) to Ord(wbkRefresh) do
begin
if TWWBtnKind(i) in value then
inc(shouldBeVisibleCount);
end;
Left := LeftPos;
LeftPos := LeftPos + FButtonWidth;
procedure TWWDBNavigator.RedrawButton;
var
i:integer;
begin
if Assigned(FImages) then
begin
i := 0;
while (i <= ord(wbkRefresh)) and (i < FImages.Count) do
begin
FImages.GetBitmap(i,
FBtnNavigators[TWWBtnKind(i)].Glyph);
Inc(i);
end;
Self.Invalidate;
end
else ClearButton;
end;
procedure TWWDBNavigator.ClearButton;
var
procedure TWWDBNavigator.SetLayout(aLayout:TButtonLayout);
var
i:TWWBtnKind;
begin
for i:=Low(FBtnNavigators) to High(FBtnNavigators) do
FBtnNavigators[i].Layout := aLayout;
FLayout := aLayout;
end;
procedure TWWDBNavigator.DoNavigatorResize(Sender:TObject);
vaR
leftPos, i:integer;
begin
Width := inherited Width;
Height:= inherited Height;
leftPos := 0;
Left := LeftPos;
LeftPos := LeftPos + FButtonWidth;
end
destructor TWWDBNavigator.Destroy;
var
i:integer;
begin
for i:=Ord(wbkFirst) to Ord(wbkRefresh) do
FBtnNavigators[TWWBtnKind(i)].Free;
FHints.Free;
FCaptions.Free;
FDataLink.Free;
FDataLink := Nil;
inherited Destroy;
end;
{ TWWDataLink }
constructor TWWDataLink.Create(ANav: TWWDBNavigator);
begin
inherited Create;
FNavigator := ANav;
VisualControl := True;
end;
procedure TWWDataLink.EditingChanged;
begin
if FNavigator <> nil then FNavigator.EditingChanged;
end;
procedure TWWDataLink.DataSetChanged;
begin
if FNavigator <> nil then FNavigator.DataChanged;
end;
procedure TWWDataLink.ActiveChanged;
begin
if FNavigator <> nil then FNavigator.ActiveChanged;
end;
destructor TWWDataLink.Destroy;
begin
FNavigator := nil;
inherited Destroy;
end;
procedure TWWDBNavigator.SetIndonesianLanguage(
newHints := TStringList.Create;
if value then
begin
with newHints do
begin
Add('Awal data');
Add('Data sebelumnya');
Add('Data berikutnya');
Add('Akhir data');
Add('Tambah data');
Add('Hapus data');
Add('Edit data');
Add('Simpan data');
Add('Batalkan perubahan');
Add('Baca ulang data');
end;
SetHints(newHints);
msg[wbkFirst] := 'Awal';
msg[wbkPrior] := 'Maju';
msg[wbkNext] := 'Mundur';
msg[wbkLast] := 'Akhir';
msg[wbkInsert] := 'Tambah';
msg[wbkDelete] := 'Hapus';
msg[wbkPost] := 'Simpan';
msg[wbkCancel] := 'Batal';
msg[wbkRefresh] := 'Baca Ulang';
msg[wbkEdit] := 'Edit';
end
else
begin
with newHints do
begin
Add('First record');
Add('Previous record');
Add('Next record');
Add('Last record');
Add('Insert record');
Add('Delete record');
Add('Edit record');
Add('Save record');
Add('Cancel record');
Add('Refresh record');
end;
SetHints(newHints);
msg[wbkFirst] := 'First';
FCaptions.Clear;
if FCaptionEnabled then
begin
for i:=Ord(Low(FBtnNavigators)) to Ord(High(FBtnNavigators))
do
FCaptions.Add(msg[TWWBtnKind(i)]);
end;
end;
end.
Membuat event seperti pada contoh di atas sebenarnya cukup mudah. Jika masih belum
terlalu jelas dengan contoh di atas, mari kita lihat contoh pembuatan event yang lebih
sederhana. Event sebenarnya merupakan pointer ke method / prosedure. Pada contoh unit
classPersegiPanjang, misalkan kita akan membuat event SebelumPanjangBerubah, yang
akan terpicu sebelum panjang persegi panjang diubah. Pertama kita harus membuat tipe
data untuk event yang akan ditrigger, dan parameter yang akan muncul pada event
tersebut. Misal kita hanya akan mengirimkan objek persegi panjang yang dibuat dan
panjangnya. Maka kita membuat signature dari event yang akan kita buat terdiri atas
Sender dengan tipe TObject.
TPersegiPanjangEvent = procedure (Sender:TObject; Panjang:double) of
Object;
Berikutnya kita perlu memanggil event ini, persis sebelum panjang diubah. Dengan
catatan kalau programmer yang menggunakan komponen kita sudah memasukkan kode
dalam event SebelumPanjangBerubah. Jika tidak, tidak usah dipanggil.
procedure TPersegiPanjang.SetPanjang(const Value: double);
begin
if Assigned(FPersegiPanjangEvent) then
FPersegiPanjangEvent(Self, Value);
FPanjang := Value;
end;